package com.hardson.toolkit.util;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Objects;
import java.util.regex.Pattern;

public class MathUtil {

    // 默认除法运算精度
    private static final int DEF_DIV_SCALE = 10;

    // 这个类不能实例化
    private MathUtil() {

    }

    /**
     * 提供精确的加法运算。
     *
     * @param v1 被加数
     * @param v2 加数
     * @return 两个参数的和
     */
    public static double add(final double v1, final double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.add(b2).doubleValue();
    }

    public static double add(final Double v1, final Double v2) {
        BigDecimal b1 = new BigDecimal(v1.toString());
        BigDecimal b2 = new BigDecimal(v2.toString());
        return b1.add(b2).doubleValue();
    }

    /**
     * 提供精确的减法运算。
     *
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     */
    public static double sub(final double v1, final double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.subtract(b2).doubleValue();
    }

    public static double sub(final Double v1, final Double v2) {
        BigDecimal b1 = new BigDecimal(v1.toString());
        BigDecimal b2 = new BigDecimal(v2.toString());
        return b1.subtract(b2).doubleValue();
    }

    /**
     * 提供精确的乘法运算。
     *
     * @param v1 被乘数
     * @param v2 乘数
     * @return 两个参数的积
     */
    public static double mul(final double v1, final double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.multiply(b2).doubleValue();
    }

    public static double mul(final Double v1, final Double v2) {
        BigDecimal b1 = new BigDecimal(v1.toString());
        BigDecimal b2 = new BigDecimal(v2.toString());
        return b1.multiply(b2).doubleValue();
    }

    /**
     * 提供（相对）精确的除法运算，当发生除不尽的情况时，精确到 小数点以后10位，以后的数字四舍五入。
     *
     * @param v1 被除数
     * @param v2 除数
     * @return 两个参数的商
     */
    public static double div(final double v1, final double v2) {
        return div(v1, v2, DEF_DIV_SCALE);
    }

    public static double div(final Double v1, final Double v2) {
        return div(v1, v2, DEF_DIV_SCALE);
    }

    /**
     * 提供（相对）精确的除法运算。当发生除不尽的情况时，由scale参数指 定精度，以后的数字四舍五入。
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     */
    public static double div(final double v1, final double v2, final int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue();
    }

    public static double div(final Double v1, final Double v2, final int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1.toString());
        BigDecimal b2 = new BigDecimal(v2.toString());
        return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 提供精确的小数位四舍五入处理。
     *
     * @param v     需要四舍五入的数字
     * @param scale 小数点后保留几位
     * @return 四舍五入后的结果
     */
    public static double round(final double v, final int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b = new BigDecimal(Double.toString(v));
        BigDecimal one = new BigDecimal("1");
        return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue();
    }

    public static double round(final Double v, final int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b = new BigDecimal(v.toString());
        BigDecimal one = new BigDecimal("1");
        return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 判断是否全由0~9的数字构成
     *
     * @param obj 要鉴别的对象
     * @return boolean
     */
    public static boolean isNumerics(final Object obj) {
        return Pattern.matches("[0-9]+", Objects.toString(obj, ""));
    }

    /**
     * 判断是否为数值（包括0和正负整数、正负小数）
     *
     * @param obj 要鉴别的对象
     * @return boolean
     */
    public static boolean isRealNumber(Object obj) {
        return Pattern.matches("^[+-]?\\d+(\\.\\d+)?$", Objects.toString(obj, ""));
    }

    /**
     * 将对象字符串（toString()）转换在int，如果传入对象字符串不是数字，则返回替代整数
     *
     * @param obj             要转换的对象
     * @param replaceIfNotInt 转换的对象不是数字时的替代整数
     * @return int
     */
    public static int parseInt(Object obj, int replaceIfNotInt) {
        return isNumerics(obj) ? Integer.parseInt(obj.toString()) : replaceIfNotInt;
    }

    /**
     * 限定为指定范围内的数值。
     *
     * @param num      被检查的原数值
     * @param leftNum  数值下限
     * @param rightNum 数值上限
     * @return 如果给定的数值小于下限，则返回下限值;如果大于上限，则返回上限值;否则返回原数值
     */
    public static Number between(Number num, Number leftNum, Number rightNum) {
        double doubleNum = num.doubleValue();
        double left = leftNum.doubleValue();
        double right = rightNum.doubleValue();
        if (doubleNum < left) {
            return left;
        }
        if (doubleNum > right) {
            return right;
        }
        return num;
    }

    public static int between(int num, int min, int max) {
        if (num < min) {
            return min;
        }
        return Math.min(num, max);
    }

    public static boolean isBetween(int num, int leftNum, int rightNum) {
        return num > leftNum && num < rightNum;
    }

    public static boolean isBetweenEq(int num, int leftNum, int rightNum) {
        return num >= leftNum && num <= rightNum;
    }

    public static boolean isBetween(Number num, Number leftNum, Number rightNum) {
        double doubleNum = num.doubleValue();
        return doubleNum > leftNum.doubleValue() && doubleNum < rightNum.doubleValue();
    }

    public static boolean isBetween(String numStr, Number leftNum, Number rightNum) {
        if (!MathUtil.isNumerics(numStr)) {
            return false;
        }
        return isBetween(Double.valueOf(numStr), leftNum, rightNum);
    }

    public static boolean isBetweenEq(Number num, Number leftNum, Number rightNum) {
        double doubleNum = num.doubleValue();
        return doubleNum >= leftNum.doubleValue() && doubleNum <= rightNum.doubleValue();
    }

    public static boolean isBetweenEq(String numStr, Number leftNum, Number rightNum) {
        if (!MathUtil.isNumerics(numStr)) {
            return false;
        }
        return isBetweenEq(Double.valueOf(numStr), leftNum, rightNum);
    }

    public static int choosePositive(int num, int defaultNum) {
        return num < 0 ? defaultNum : num;
    }

    /**
     * 判断是否为奇数
     *
     * @param number 被判断的数字
     * @return boolean是否为奇数
     */
    public static boolean isOdd(int number) {
        return number % 2 != 0;
    }

    /**
     * 判断是否为偶数
     *
     * @param number 被判断的数字
     * @return boolean是否为偶数
     */
    public static boolean isEven(int number) {
        return !isOdd(number);
    }
}